﻿using System;
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
using JetBrains.ReSharper.Psi;
using JetBrains.ReSharper.Psi.CodeStyle;
using JetBrains.ReSharper.Psi.CSharp;
using JetBrains.ReSharper.Psi.CSharp.Tree;
using JetBrains.ReSharper.Psi.ExtensionsAPI.Tree;
using JetBrains.ReSharper.Psi.Tree;
using JetBrains.Util;

namespace MugenMvvmToolkit.ReSharper.Infrastructure
{
    public class RegionWrapper
    {
        #region Fields

        private readonly IClassLikeDeclaration _classDeclaration;
        private readonly string _regionName;
        private readonly CSharpElementFactory _elementFactory;

        #endregion

        #region Constructors

        /// <summary>
        ///     Initializes a new instance of the <see cref="RegionWrapper" /> class.
        /// </summary>
        public RegionWrapper([NotNull] IClassLikeDeclaration classDeclaration, [NotNull] string regionName,
            [NotNull] CSharpElementFactory elementFactory)
        {
            if (classDeclaration == null) throw new ArgumentNullException("classDeclaration");
            if (regionName == null) throw new ArgumentNullException("regionName");
            if (elementFactory == null) throw new ArgumentNullException("elementFactory");
            _classDeclaration = classDeclaration;
            _regionName = regionName;
            _elementFactory = elementFactory;
        }

        #endregion

        #region Methods

        public void Wrap(IList<IPropertyDeclaration> properties)
        {
            var region = TryFindRegion();
            if (region.A == null)
            {
                region = _elementFactory.CreateRegionDirective(_regionName);
                ModificationUtil.AddChildBefore(properties.First(), region.A);
                ModificationUtil.AddChildAfter(properties.Last(), region.B);
            }
            else
            {
                for (int index = 0; index < properties.Count; index++)
                {
                    var propertyDeclaration = properties[index];
                    properties[index] = ModificationUtil.AddChildAfter(_classDeclaration, region.A, propertyDeclaration);
                    ModificationUtil.DeleteChild(propertyDeclaration);
                }
            }
            var languageService = _classDeclaration.Language.LanguageService();
            if (languageService != null && languageService.CodeFormatter != null)
            {
                ITreeNode lastElement = region.B ?? (ITreeNode)properties.Last();
                languageService.CodeFormatter.Format(region.A, lastElement, CodeFormatProfile.DEFAULT);
            }
        }

        private JetTuple<IStartRegion, IEndRegion> TryFindRegion()
        {
            IStartRegion startRegion = null;
            IEndRegion endRegion = null;
            _classDeclaration.ProcessDescendants(new OneActionProcessor(node =>
            {
                var st = node as IStartRegion;
                if (st != null && st.Name == _regionName)
                    startRegion = st;
                if (startRegion != null)
                {
                    endRegion = node as IEndRegion;
                }
            }, node => startRegion == null && endRegion == null));
            return JetTuple.Of(startRegion, endRegion);
        }

        #endregion
    }
}